/*
 * MIT License
 *
 * Copyright (c) 2020 wen.gu <454727014@qq.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

 /***************************************************************************
 * Name: posix_atomic.c
 *
 * Purpose: implementation atomic operation with posix API
 *
 * Developer:
 *   wen.gu , 2022-01-21
 *
 * TODO:
 *
 ***************************************************************************/

 /******************************************************************************
 **    INCLUDES
 ******************************************************************************/
#include "config.h"
#if defined(HAS_STD_ATOMIC) && (HAS_STD_ATOMIC) 
#include "cbus/core/cbus_atomic.h"

#include <stdatomic.h>

#include "cbus/core/cbus_log.h"


/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/


/******************************************************************************
 **    inner FUNCTION DEFINITIONS
 ******************************************************************************/

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
/**
*
*@brief  add a value to org_value as atomic operation
*
*@param  org_value [io] origin value.
*@param  add_value [in] add value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_exchange_add(cbus_atomic_int *org_value, cbus_atomic_int add_value)
{
    return atomic_fetch_add(org_value, add_value);
}

/**
*
*@brief  add 1 to org_value as atomic operation
*
*@param  org_value [io] origin value.
*
*@return  previous *org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_increment(cbus_atomic_int *org_value)
{
    return atomic_fetch_add(org_value, 1);
}

/**
*
*@brief  dec  1 to org_value as atomic operation
*
*@param  org_value [io] origin value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_decrement(cbus_atomic_int *org_value)
{
    return atomic_fetch_sub(org_value, 1);
}

/**
*
*@brief  atomic 'or' operation
*
*@param  org_value [io] origin value.
*@param  value     [in]  a value to 'or' with org_value.
*
*@return previous org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_or(cbus_atomic_int *org_value, cbus_atomic_int value)
{
    return atomic_fetch_or(org_value, value);
}

/**
*
*@brief  atomic 'and' operation
*
*@param  org_value [io] origin value.
*@param  value     [in]  a value to 'and' with org_value.
*
*@return previous org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_and(cbus_atomic_int *org_value, cbus_atomic_int value)
{
    return atomic_fetch_and(org_value, value);
}

/**
 *
 *@brief  compare org_value with old_value, if equal then org_value with new_value
 *
 *@param  org_value  [io] origin value.
 *        old_value [in] old value.
 *        new_value [in] new value.
 *
 *@return This returns GTRUE if the new value was successfully stored, 
 *        which will only happen when *org_value == old_value.
 *
 *@see
 */
GBOL cbus_atomic_compare_exchange(cbus_atomic_int *org_value, cbus_atomic_int old_value, cbus_atomic_int new_value)
{
    return atomic_compare_exchange_strong(org_value, old_value, new_value) ? GTRUE : GFALSE;
}

//////////////////////////////////////////////////////////////////////////

/**
*
*@brief  add a value to org_value as atomic operation(org_value is 64 bit)
*
*@param  org_value [io] origin value.
*@param  add_value [in] add value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int64 cbus_atomic_exchange_add64(cbus_atomic_int64 *org_value, cbus_atomic_int64 add_value)
{
    return atomic_fetch_add(org_value, add_value);
}

/**
*
*@brief  add 1 to org_value as atomic operation(org_value is 64 bit)
*
*@param  org_value [io] origin value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int64 cbus_atomic_increment64(cbus_atomic_int64 *org_value)
{
    return atomic_fetch_add(org_value, 1);
}

/**
*
*@brief  dec  1 to org_value as atomic operation(org_value is 64 bit)
*
*@param  org_value [io] origin value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int64 cbus_atomic_decrement64(cbus_atomic_int64 *org_value)
{
    return atomic_fetch_sub(org_value, 1);
}

#elif defined(__GNUC__)
#include "cbus/core/cbus_atomic.h"
/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

/******************************************************************************
 **    inner FUNCTION DEFINITIONS
 ******************************************************************************/

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
/**
*
*@brief  add a value to org_value as atomic operation
*
*@param  org_value [io] origin value.
*@param  add_value [in] add value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_exchange_add(cbus_atomic_int *org_value, cbus_atomic_int add_value)
{
    return __sync_fetch_and_add(org_value, add_value);
}

/**
*
*@brief  add 1 to org_value as atomic operation
*
*@param  org_value [io] origin value.
*
*@return  previous *org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_increment(cbus_atomic_int *org_value)
{
    return __sync_fetch_and_add(org_value, 1);
}

/**
*
*@brief  dec  1 to org_value as atomic operation
*
*@param  org_value [io] origin value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_decrement(cbus_atomic_int *org_value)
{
    return __sync_fetch_and_sub (org_value, 1);
}

/**
*
*@brief  atomic 'or' operation
*
*@param  org_value [io] origin value.
*@param  value     [in]  a value to 'or' with org_value.
*
*@return previous org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_or(cbus_atomic_int *org_value, cbus_atomic_int value)
{
    return  __sync_fetch_and_or(org_value, value);
}

/**
*
*@brief  atomic 'and' operation
*
*@param  org_value [io] origin value.
*@param  value     [in]  a value to 'and' with org_value.
*
*@return previous org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_and(cbus_atomic_int *org_value, cbus_atomic_int value)
{
        return  __sync_fetch_and_and(org_value, value);
}

/**
 *
 *@brief  compare org_value with old_value, if equal then org_value with new_value
 *
 *@param  org_value  [io] origin value.
 *        old_value [in] old value.
 *        new_value [in] new value.
 *
 *@return This returns GTRUE if the new value was successfully stored, 
 *        which will only happen when *org_value == old_value.
 *
 *@see
 */
GBOL cbus_atomic_compare_exchange(cbus_atomic_int *org_value, cbus_atomic_int old_value, cbus_atomic_int new_value)
{
    return  __sync_bool_compare_and_swap(org_value, old_value, new_value) ? GTRUE : GFALSE;
}

//////////////////////////////////////////////////////////////////////////

/**
*
*@brief  add a value to org_value as atomic operation(org_value is 64 bit)
*
*@param  org_value [io] origin value.
*@param  add_value [in] add value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int64 cbus_atomic_exchange_add64(cbus_atomic_int64 *org_value, cbus_atomic_int64 add_value)
{
    return  __sync_fetch_and_add(org_value, add_value);
}

/**
*
*@brief  add 1 to org_value as atomic operation(org_value is 64 bit)
*
*@param  org_value [io] origin value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int64 cbus_atomic_increment64(cbus_atomic_int64 *org_value)
{
    return  __sync_fetch_and_add(org_value, 1);
}

/**
*
*@brief  dec  1 to org_value as atomic operation(org_value is 64 bit)
*
*@param  org_value [io] origin value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int64 cbus_atomic_decrement64(cbus_atomic_int64 *org_value)
{
    return  __sync_fetch_and_sub(org_value, 1);
}

#elif defined(HAS_POSIX_MUTEX_API) && (HAS_POSIX_MUTEX_API) 
#include "cbus/core/cbus_atomic.h"

#include <pthread.h>

/******************************************************************************
 **    MACROS
 ******************************************************************************/

/******************************************************************************
 **    VARIABLE DEFINITIONS
 ******************************************************************************/

static pthread_mutex_t g_atomic_lock = PTHREAD_MUTEX_INITIALIZER;
/******************************************************************************
 **    inner FUNCTION DEFINITIONS
 ******************************************************************************/

/******************************************************************************
 **    FUNCTION DEFINITIONS
 ******************************************************************************/
/**
*
*@brief  add a value to org_value as atomic operation
*
*@param  org_value [io] origin value.
*@param  add_value [in] add value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_exchange_add(cbus_atomic_int *org_value, cbus_atomic_int add_value)
{
    cbus_atomic_int result;
    pthread_mutex_lock(&g_atomic_lock);
    result = *org_value;
    *org_value += add_value;
    pthread_mutex_unlock(&g_atomic_lock);
    return result;
}

/**
*
*@brief  add 1 to org_value as atomic operation
*
*@param  org_value [io] origin value.
*
*@return  previous *org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_increment(cbus_atomic_int *org_value)
{
    return cbus_atomic_exchange_add(org_value, 1);
}

/**
*
*@brief  dec  1 to org_value as atomic operation
*
*@param  org_value [io] origin value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_decrement(cbus_atomic_int *org_value)
{
    return cbus_atomic_exchange_add(org_value, -1);
}

/**
*
*@brief  atomic 'or' operation
*
*@param  org_value [io] origin value.
*@param  value     [in]  a value to 'or' with org_value.
*
*@return previous org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_or(cbus_atomic_int *org_value, cbus_atomic_int value)
{
    cbus_atomic_int result;
    pthread_mutex_lock(&g_atomic_lock);
    result = *org_value;
    *org_value = result | value;
    pthread_mutex_unlock(&g_atomic_lock);
    return result;
}

/**
*
*@brief  atomic 'and' operation
*
*@param  org_value [io] origin value.
*@param  value     [in]  a value to 'and' with org_value.
*
*@return previous org_value.
*
*@see
*/
cbus_atomic_int cbus_atomic_and(cbus_atomic_int *org_value, cbus_atomic_int value)
{
    cbus_atomic_int result;
    pthread_mutex_lock(&g_atomic_lock);
    result = *org_value;
    *org_value = result & value;
    pthread_mutex_unlock(&g_atomic_lock);
    return result;
}

/**
 *
 *@brief  compare org_value with old_value, if equal then org_value with new_value
 *
 *@param  org_value  [io] origin value.
 *        old_value [in] old value.
 *        new_value [in] new value.
 *
 *@return This returns GTRUE if the new value was successfully stored, 
 *        which will only happen when *org_value == old_value.
 *
 *@see
 */
GBOL cbus_atomic_compare_exchange(cbus_atomic_int *org_value, cbus_atomic_int old_value, cbus_atomic_int new_value)
{
    GBOL result = GFALSE;
    pthread_mutex_lock(&g_atomic_lock);
    if (*org_value == old_value)
    {
        *org_value = new_value;
        result = GTRUE;
    }
    pthread_mutex_unlock(&g_atomic_lock);
    return result;
}

//////////////////////////////////////////////////////////////////////////

/**
*
*@brief  add a value to org_value as atomic operation(org_value is 64 bit)
*
*@param  org_value [io] origin value.
*@param  add_value [in] add value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int64 cbus_atomic_exchange_add64(cbus_atomic_int64 *org_value, cbus_atomic_int64 add_value)
{
    cbus_atomic_int64 result;
    pthread_mutex_lock(&g_atomic_lock);
    result = *org_value;
    *org_value = result + add_value;
    pthread_mutex_unlock(&g_atomic_lock);
    return result;
}

/**
*
*@brief  add 1 to org_value as atomic operation(org_value is 64 bit)
*
*@param  org_value [io] origin value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int64 cbus_atomic_increment64(cbus_atomic_int64 *org_value)
{
    return cbus_atomic_exchange_add64(org_value, 1);
}

/**
*
*@brief  dec  1 to org_value as atomic operation(org_value is 64 bit)
*
*@param  org_value [io] origin value.
*
*@return previous *org_value.
*
*@see
*/
cbus_atomic_int64 cbus_atomic_decrement64(cbus_atomic_int64 *org_value)
{
    return cbus_atomic_exchange_add64(org_value, -1);
}
#else

#endif /** defined HAS_STD_ATOMIC */